#!/bin/bash

# Redis集群自动化部署脚本 - 由 Logan.Li 优化
set -e

# 配置参数
REDIS_VERSION="6.2.13"
# REDIS_PASSWORD="redis@pwdoAo"  # 请修改为强密码
REDIS_PORTS=(6001 6002 6003 6004 6005 6006)
CLUSTER_REPLICAS=1
SSH_USER="root"  # 使用root用户执行
HOST_INFO_FILE="redis-info.txt"  # 主机信息文件

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 显示使用说明
usage() {
    echo "使用方法: $0 [选项]"
    echo "选项:"
    echo "  --single <本机IP>          单机部署 (所有节点在同一台服务器)"
    echo "  --multi <IP1> <IP2> <IP3>  多机部署 (三台服务器)"
    echo "  --create-cluster <IP1> <IP2> <IP3>  仅创建集群 (跳过编译安装步骤)"
    echo "  --help                     显示帮助信息"
    echo ""
    echo "示例:"
    echo "  单机部署: $0 --single 192.168.1.100"
    echo "  多机部署: $0 --multi 192.168.1.101 192.168.1.102 192.168.1.103"
    echo "  仅创建集群: $0 --create-cluster 192.168.1.101 192.168.1.102 192.168.1.103"
    exit 1
}

# 检查SSH免密登录
check_ssh() {
    local server=$1
    log_info "检查到服务器 $server 的SSH连接..."
    
    if ! ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no ${SSH_USER}@${server} "echo SSH连接成功" &>/dev/null; then
        log_error "无法SSH免密登录到 $server"
        log_error "请先设置SSH免密登录: ssh-copy-id ${SSH_USER}@${server}"
        return 1
    else
        log_info "成功连接到服务器 $server"
    fi
    return 0
}

# 安装依赖函数
install_dependencies() {
    local server=$1
    log_info "在 $server 上安装系统依赖..."
    
    if ssh ${SSH_USER}@${server} "command -v yum &> /dev/null"; then
        # Amazon Linux / CentOS / RHEL
        ssh ${SSH_USER}@${server} "yum update -y && yum install -y gcc make openssl-devel systemd-devel wget tar"
    elif ssh ${SSH_USER}@${server} "command -v apt-get &> /dev/null"; then
        # Ubuntu / Debian
        ssh ${SSH_USER}@${server} "apt update && apt install -y build-essential libssl-dev libsystemd-dev wget tcl"
    else
        log_error "不支持的包管理器"
        return 1
    fi
}

# 创建Redis用户和目录
setup_environment() {
    local server=$1
    log_info "在 $server 上设置Redis用户和目录..."
    
    ssh ${SSH_USER}@${server} << 'EOF'
if ! id "redis" &>/dev/null; then
    groupadd -r redis
    useradd -r -g redis -s /bin/false -d /var/lib/redis redis
fi

mkdir -p /opt/redis/{data,logs,conf}
chown -R redis:redis /opt/redis
EOF
}

# 下载和编译Redis
install_redis() {
    local server=$1
    local is_master=${2:-true}  # 默认为主节点编译
    
    if [ "$is_master" = true ]; then
        log_info "在 $server 上下载和编译Redis ${REDIS_VERSION}..."
        
        ssh ${SSH_USER}@${server} << EOF
cd /tmp
if [ ! -f "redis-${REDIS_VERSION}.tar.gz" ]; then
    wget "https://download.redis.io/releases/redis-${REDIS_VERSION}.tar.gz"
fi
tar xzf "redis-${REDIS_VERSION}.tar.gz"
cd "redis-${REDIS_VERSION}"

make -j\$(nproc) BUILD_TLS=yes USE_SYSTEMD=yes
make install PREFIX=/usr/local

# 创建二进制文件压缩包，方便分发到其他节点
cd /usr/local/bin
tar czf /tmp/redis-bin.tar.gz redis-server redis-cli redis-benchmark redis-check-aof redis-check-rdb

echo "Redis安装完成，二进制文件已打包"
EOF
    else
        log_info "从主节点复制Redis二进制文件到 $server..."
        
        # 从主节点复制二进制文件包到当前节点
        scp ${SSH_USER}@${SERVER_LIST[0]}:/tmp/redis-bin.tar.gz /tmp/
        
        # 在目标节点上解压二进制文件
        ssh ${SSH_USER}@${server} << EOF
mkdir -p /usr/local/bin
tar xzf /tmp/redis-bin.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/redis-*
echo "Redis二进制文件已安装"
EOF
    fi
}

# 生成Redis配置文件
generate_config() {
    local server=$1
    local port=$2
    log_info "在 $server 上生成Redis配置 ${port}..."
    
    ssh ${SSH_USER}@${server} << EOF
tee /opt/redis/conf/redis-${port}.conf > /dev/null << CONF_EOF
port ${port}
cluster-enabled yes
cluster-config-file nodes-${port}.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
supervised systemd
protected-mode no
pidfile /var/run/redis_${port}.pid
logfile /opt/redis/logs/redis_${port}.log
dir /opt/redis/data/${port}
maxmemory 2gb
maxmemory-policy allkeys-lru
tls-cluster no
tls-replication no
CONF_EOF

mkdir -p /opt/redis/data/${port}
chown -R redis:redis /opt/redis
EOF
}

# 创建Systemd服务
create_systemd_service() {
    local server=$1
    local port=$2
    log_info "在 $server 上创建Systemd服务 redis-${port}..."
    
    ssh ${SSH_USER}@${server} << EOF
tee /etc/systemd/system/redis-${port}.service > /dev/null << SERVICE_EOF
[Unit]
Description=Redis Cluster Node on port ${port}
After=network.target

[Service]
Type=notify
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /opt/redis/conf/redis-${port}.conf
RuntimeDirectory=redis-${port}
RuntimeDirectoryMode=0755
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
SERVICE_EOF
EOF
}

# 启动Redis服务
start_redis_services() {
    local server=$1
    local ports=$2
    log_info "在 $server 上启动Redis服务..."
    
    ssh ${SSH_USER}@${server} "systemctl daemon-reload"
    
    for port in ${ports}; do
        ssh ${SSH_USER}@${server} "systemctl enable redis-${port} && systemctl start redis-${port}"
        log_info "端口 ${port} 服务状态: $(ssh ${SSH_USER}@${server} "systemctl is-active redis-${port}")"
    done
}



# 创建Redis集群
create_cluster() {
    local nodes=$1
    log_info "创建Redis集群..."
    
    # 构建redis-cli命令 -a \"${REDIS_PASSWORD}\"
     local cluster_cmd="/usr/local/bin/redis-cli  --cluster create ${nodes} --cluster-replicas ${CLUSTER_REPLICAS} --cluster-yes"
    
    # 在第一台服务器上执行集群创建命令
    ssh ${SSH_USER}@${SERVER_LIST[0]} "${cluster_cmd}"
}

# 验证集群状态
verify_cluster() {
    local server=$1
    local port=$2
    log_info "验证集群状态..."
    
    ssh ${SSH_USER}@${server} << EOF
echo "集群信息:"
/usr/local/bin/redis-cli -c -p ${port} cluster info

echo -e "\n节点信息:"
/usr/local/bin/redis-cli -c -p ${port} cluster nodes

echo -e "\n测试集群操作:"
/usr/local/bin/redis-cli -c -p ${port} << REDIS_EOF
set test_key "集群测试值"
get test_key
REDIS_EOF
EOF
}

# 单机部署函数
deploy_single() {
    local server_ip=$1
    log_info "开始单机部署到: ${server_ip}"
    
    # 检查SSH连接
    check_ssh ${server_ip} || exit 1
    
    # 安装依赖
    install_dependencies ${server_ip}
    
    # 设置环境
    setup_environment ${server_ip}
    
    # 安装Redis
    install_redis ${server_ip}
    
    # 生成配置和创建服务
    for port in "${REDIS_PORTS[@]}"; do
        generate_config ${server_ip} ${port}
        create_systemd_service ${server_ip} ${port}
    done
    
    # 启动服务
    start_redis_services ${server_ip} "${REDIS_PORTS[*]}"
    
    # 等待所有节点启动
    sleep 5
    
    # 创建集群
    local nodes=""
    for port in "${REDIS_PORTS[@]}"; do
        nodes="${nodes} ${server_ip}:${port}"
    done
    create_cluster "${nodes}"
    
    # 验证集群
    sleep 3
    verify_cluster ${server_ip} ${REDIS_PORTS[0]}
    
    log_info "单机Redis集群部署完成!"
}

# 创建主机信息文件
create_host_info() {
    local servers=($@)
    log_info "创建主机信息文件: ${HOST_INFO_FILE}"
    
    # 创建主机信息文件
    cat > ${HOST_INFO_FILE} << EOF
# Redis集群主机信息 - 由 Logan.Li 创建于 $(date '+%Y-%m-%d %H:%M:%S')
# 格式: IP地址 角色 端口

# 主节点
${servers[0]} master 6001
${servers[1]} master 6003
${servers[2]} master 6005

# 从节点
${servers[0]} slave 6002
${servers[1]} slave 6004
${servers[2]} slave 6006
EOF
    
    log_info "主机信息文件创建完成，内容如下:"
    cat ${HOST_INFO_FILE}
}

# 仅创建集群函数
create_cluster_only() {
    local servers=($@)
    log_info "仅创建集群 (跳过编译安装) 到: ${servers[*]}"
    
    # 创建主机信息文件
    create_host_info "${servers[@]}"
    
    # 检查所有服务器的SSH连接
    for server in "${servers[@]}"; do
        check_ssh ${server} || exit 1
    done
    
    # 节点分配: 每台服务器运行两个实例
    local port_assignments=(
        "${servers[0]}:6001 ${servers[0]}:6002"  # 服务器1: 6001, 6002
        "${servers[1]}:6003 ${servers[1]}:6004"  # 服务器2: 6003, 6004
        "${servers[2]}:6005 ${servers[2]}:6006"  # 服务器3: 6005, 6006
    )
    
    # 等待所有节点启动
    log_info "确保所有Redis服务已启动..."
    sleep 5
    
    # 创建集群
    local nodes=""
    for assignment in "${port_assignments[@]}"; do
        nodes="${nodes} ${assignment}"
    done
    
    log_info "AWS安全组提示: 请确保已在AWS安全组中开放了以下端口:"
    log_info "- TCP 6001-6006: Redis服务端口"
    log_info "- TCP 16001-16006: Redis集群总线端口"
    
    create_cluster "${nodes}"
    
    # 验证集群
    sleep 3
    verify_cluster ${servers[0]} 6001
    
    log_info "Redis集群创建完成!"
}

# 多机部署函数
deploy_multi() {
    local servers=($@)
    log_info "开始多机部署到: ${servers[*]}"
    
    # 创建主机信息文件
    create_host_info "${servers[@]}"
    
    # 检查所有服务器的SSH连接
    for server in "${servers[@]}"; do
        check_ssh ${server} || exit 1
    done
    
    # 节点分配: 每台服务器运行两个实例
    local port_assignments=(
        "${servers[0]}:6001 ${servers[0]}:6002"  # 服务器1: 6001, 6002
        "${servers[1]}:6003 ${servers[1]}:6004"  # 服务器2: 6003, 6004
        "${servers[2]}:6005 ${servers[2]}:6006"  # 服务器3: 6005, 6006
    )
    
    # 在所有服务器上部署Redis
    for i in "${!servers[@]}"; do
        local server=${servers[$i]}
        log_info "在服务器 ${server} 上部署Redis..."
        
        # 安装依赖
        install_dependencies ${server}
        
        # 设置环境
        setup_environment ${server}
        
        # 安装Redis - 只在第一台服务器编译，其他服务器复制二进制文件
        if [ $i -eq 0 ]; then
            install_redis ${server} true
        else
            install_redis ${server} false
        fi
    done
    
    # 为每台服务器配置和启动指定的端口
    for i in "${!servers[@]}"; do
        local server=${servers[$i]}
        local ports_str=${port_assignments[$i]}
        local ports=($ports_str)
        
        # 提取端口号
        local port1=$(echo ${ports[0]} | cut -d: -f2)
        local port2=$(echo ${ports[1]} | cut -d: -f2)
        
        # 生成配置和创建服务
        generate_config ${server} ${port1}
        generate_config ${server} ${port2}
        create_systemd_service ${server} ${port1}
        create_systemd_service ${server} ${port2}
        
        # 启动服务
        start_redis_services ${server} "${port1} ${port2}"
    done
    
    # 等待所有节点启动
    sleep 5
    
    # 创建集群
    local nodes=""
    for assignment in "${port_assignments[@]}"; do
        nodes="${nodes} ${assignment}"
    done
    create_cluster "${nodes}"
    
    # 验证集群
    sleep 3
    verify_cluster ${servers[0]} 6001
    
    log_info "多机Redis集群部署完成!"
}

# 检查脚本是否以root身份运行
check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_error "此脚本必须以root身份运行"
        log_error "请使用: sudo $0 [选项]"
        exit 1
    fi
}

# 主执行逻辑
main() {
    # 检查root权限
    check_root
    
    # 检查参数
    if [[ $# -lt 1 ]]; then
        usage
    fi
    
    # 解析参数
    case "$1" in
        --single)
            if [[ $# -ne 2 ]]; then
                log_error "--single 参数需要指定本机IP"
                usage
            fi
            SERVER_IP=$2
            deploy_single ${SERVER_IP}
            ;;
        --multi)
            if [[ $# -ne 4 ]]; then
                log_error "--multi 参数需要指定三个服务器IP"
                usage
            fi
            SERVER_LIST=("$2" "$3" "$4")
            deploy_multi "${SERVER_LIST[@]}"
            ;;
        --create-cluster)
            if [[ $# -ne 4 ]]; then
                log_error "--create-cluster 参数需要指定三个服务器IP"
                usage
            fi
            SERVER_LIST=("$2" "$3" "$4")
            create_cluster_only "${SERVER_LIST[@]}"
            ;;
        --help|-h)
            usage
            ;;
        *)
            log_error "未知参数: $1"
            usage
            ;;
    esac
}

# 执行主函数
main "$@"